package com.lintf.flowable.controller;

import com.lintf.common.base.AjaxResult;
import com.lintf.common.page.BaseController;
import com.lintf.common.page.TableDataInfo;
import com.lintf.flowable.vo.ProcessInstanceVo;
import com.lintf.flowable.vo.TaskVo;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.flowable.variable.api.history.HistoricVariableInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wdyin
 * @date 2024/11/13
 **/
@Slf4j
@RestController
@RequestMapping("/flowable/leave")
public class FlowableLeaveController extends BaseController {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ProcessEngine processEngine;
    /**
     * 对应leave.bpmn20.xml文件中process标签的id属性
     */
    private String processKey = "leave";

    /**
     * 2. 创建请假审批流程
     *
     * @param day        请假天数
     * @param employeeId 员工id
     * @return
     */
    @PostMapping("/start/{day}/{employeeId}")
    public AjaxResult start(@PathVariable("day") Integer day, @PathVariable("employeeId") String employeeId) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("day", day);
        variables.put("employeeId", employeeId);
        //assignee表示流程的办理人
        variables.put("assignee", employeeId);
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey, variables);
        runtimeService.updateBusinessStatus(processInstance.getId(), "未审批");
        log.info("流程实例ID：{}", processInstance.getId());
        return AjaxResult.success("流程实例ID：" + processInstance.getId());
    }

    /**
     * 3. 审批流程列表
     *
     * @return
     */
    @GetMapping("/processList")
    public TableDataInfo processList() {
        RuntimeService runtimeService = processEngine.getRuntimeService();
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().list();
        List<ProcessInstanceVo> processInstanceVos = processInstances.stream().map(processInstance -> {
            ProcessInstanceVo vo = new ProcessInstanceVo();
            vo.setId(processInstance.getId());
            vo.setBusinessStatus(processInstance.getBusinessStatus());
            vo.setStartTime(processInstance.getStartTime());
            vo.setDeploymentId(processInstance.getDeploymentId());
            vo.setProcessDefinitionId(processInstance.getProcessDefinitionId());
            vo.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
            vo.setProcessDefinitionName(processInstance.getProcessDefinitionName());
            return vo;
        }).collect(Collectors.toList());
        return getDataTable(processInstanceVos);
    }

    /**
     * 4. 提交审批流程
     *
     * @param processInstanceId 流程实例ID
     * @param employeeId        员工ID
     * @param leaderId          领导ID
     */
    @PostMapping("/submit/{processInstanceId}/{employeeId}/{leaderId}")
    public AjaxResult submit(@PathVariable("processInstanceId") String processInstanceId, @PathVariable("employeeId") String employeeId, @PathVariable("leaderId") String leaderId) {
        log.info("流程实例ID：" + processInstanceId);
        //查询自己的审批流程
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).taskAssignee(employeeId).singleResult();
        Map<String, Object> variables = new HashMap<>();
        //交给哪位领导审批
        variables.put("assignee", leaderId);
        //提交请假审批流程
        try {
            taskService.complete(task.getId(), variables);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //修改请假流程的状态
        RuntimeService runtimeService = processEngine.getRuntimeService();
        runtimeService.updateBusinessStatus(processInstanceId, "审批中");
        String result = String.format("流程id:%s,任务id:%s,状态:%s", processInstanceId, task.getId(), "审批中");
        log.info(result);
        return AjaxResult.success(result);
    }

    /**
     * 5. 历史流程列表
     *
     * @return
     */
    @GetMapping("/historicProcessInstanceList")
    public TableDataInfo historicProcessInstanceList() {
        HistoricProcessInstanceQuery query = processEngine.getHistoryService().createHistoricProcessInstanceQuery();
        List<HistoricProcessInstance> historicProcessInstances = query.list();
        historicProcessInstances.forEach(historicProcessInstance -> {
            log.info("历史流程列表ID：{}，流程状态：{}", historicProcessInstance.getId(), historicProcessInstance.getBusinessStatus());
        });
        List<ProcessInstanceVo> processInstanceVos = historicProcessInstances.stream().map(processInstance -> {
            ProcessInstanceVo vo = new ProcessInstanceVo();
            vo.setId(processInstance.getId());
            vo.setBusinessStatus(processInstance.getBusinessStatus());
            vo.setStartTime(processInstance.getStartTime());
            vo.setDeploymentId(processInstance.getDeploymentId());
            vo.setProcessDefinitionId(processInstance.getProcessDefinitionId());
            vo.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
            vo.setProcessDefinitionName(processInstance.getProcessDefinitionName());
            return vo;
        }).collect(Collectors.toList());
        return getDataTable(processInstanceVos);
    }

    /**
     * 6. 删除流程
     *
     * @param id 流程id
     * @return
     */
    @DeleteMapping("/process/delete/{id}")
    public AjaxResult processDelete(@PathVariable("id") String id) {
        RuntimeService runtimeService = processEngine.getRuntimeService();
        runtimeService.deleteProcessInstance(id, "删除流程");
        log.info("删除该流程{}", id);
        return AjaxResult.success("删除该流程" + id);
    }

    /**
     * 7. 领导待办任务
     *
     * @return
     */
    @GetMapping("/leaderTodoList/{leaderId}")
    public TableDataInfo leaderTodoList(@PathVariable("leaderId") String leaderId) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(leaderId).list();
        List<TaskVo> taskVoList = tasks.stream().map(task -> {
            TaskVo taskVo = new TaskVo();
            taskVo.setTaskId(task.getId());
            taskVo.setTaskName(task.getName());
            taskVo.setProcessInstanceId(task.getProcessInstanceId());
            taskVo.setProcessVariables(taskService.getVariables(task.getId()));
            return taskVo;
        }).collect(Collectors.toList());
        log.info("领导任务列表：" + taskVoList);
        return getDataTable(taskVoList);
    }

    /**
     * 8. 领导已办任务
     */
    @GetMapping("/leaderDoneList/{leaderId}")
    public TableDataInfo leaderDoneList(@PathVariable("leaderId") String leaderId) {
        List<HistoricTaskInstance> historicTaskInstanceList = processEngine.getHistoryService()
                .createHistoricTaskInstanceQuery()
                .taskAssignee(leaderId)
                .finished() // 只查询已完成的任务
                .list();

        List<TaskVo> taskVoList = historicTaskInstanceList.stream().map(historicTask -> {
            TaskVo taskVo = new TaskVo();
            taskVo.setTaskId(historicTask.getId());
            taskVo.setTaskName(historicTask.getName());
            taskVo.setProcessInstanceId(historicTask.getProcessInstanceId());

            // 使用historyService来获取历史任务实例的变量
            HistoricVariableInstanceQuery variableQuery = processEngine.getHistoryService()
                    .createHistoricVariableInstanceQuery()
                    .processInstanceId(historicTask.getProcessInstanceId())
                    .taskId(historicTask.getId());

            List<HistoricVariableInstance> variables = variableQuery.list();
            Map<String, Object> variableMap = variables.stream()
                    .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));

            // 如果需要，还可以通过processInstanceId获取全局变量
            HistoricVariableInstanceQuery globalVariableQuery = processEngine.getHistoryService()
                    .createHistoricVariableInstanceQuery()
                    .processInstanceId(historicTask.getProcessInstanceId());

            List<HistoricVariableInstance> globalVariables = globalVariableQuery.list();
            Map<String, Object> globalVariableMap = globalVariables.stream()
                    .filter(v -> !variableMap.containsKey(v.getVariableName())) // 避免重复
                    .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));

            // 合并局部变量和全局变量
            variableMap.putAll(globalVariableMap);

            taskVo.setProcessVariables(variableMap);
            return taskVo;
        }).collect(Collectors.toList());

        log.info("已办任务：" + taskVoList);
        return getDataTable(taskVoList);
    }

    /**
     * 9. 领导批准
     *
     * @param taskId 任务ID，非流程id
     */
    @PostMapping("/leaderApply/{taskId}/{leaderId}/{bossId}")
    public AjaxResult leaderApply(@PathVariable("taskId") String taskId, @PathVariable String leaderId, @PathVariable("bossId") String bossId) {
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(leaderId).singleResult();
        Map<String, Object> variables = new HashMap<>();
        variables.put("assignee", bossId);
        variables.put("result", true);
        RuntimeService runtimeService = processEngine.getRuntimeService();
        runtimeService.updateBusinessStatus(task.getProcessInstanceId(), "领导审批完成");
        taskService.complete(task.getId(), variables);
        log.info("领导审批成功任务{}", taskId);
        return AjaxResult.success("领导审批成功任务" + taskId);
    }

    /**
     * 10. 领导拒绝
     *
     * @param taskId 任务ID
     */
    @PostMapping("/leaderRefuse/{taskId}/{leaderId}")
    public AjaxResult leaderRefuse(@PathVariable("taskId") String taskId, @PathVariable String leaderId) {
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(leaderId).singleResult();
        //通过审核
        HashMap<String, Object> variables = new HashMap<>();
        variables.put("result", false);
        taskService.complete(task.getId(), variables);
        runtimeService.updateBusinessStatus(task.getProcessInstanceId(), "领导审批拒绝");
        log.info("领导审批拒绝任务{}", taskId);
        return AjaxResult.success("领导审批拒绝任务" + taskId);
    }

    /**
     * 11. 老板待办任务
     */
    @GetMapping("/bossTodoList/{bossId}")
    public TableDataInfo bossTodoList(@PathVariable String bossId) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(bossId).list();
        List<TaskVo> taskVoList = tasks.stream().map(task -> {
            TaskVo taskVO = new TaskVo();
            taskVO.setTaskId(task.getId());
            taskVO.setTaskName(task.getName());
            taskVO.setProcessVariables(taskService.getVariables(task.getId()));
            return taskVO;
        }).collect(Collectors.toList());
        log.info("老板任务列表：" + taskVoList);
        return getDataTable(taskVoList);
    }

    /**
     * 12. 老板批准
     *
     * @param taskId 任务ID，非流程id
     * @return
     */
    @PostMapping("/bossApply/{taskId}/{bossId}")
    public AjaxResult apply(@PathVariable("taskId") String taskId, @PathVariable String bossId) {
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(bossId).singleResult();
        if (task == null) {
            log.info("老板没有任务");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", true);
        String processInstanceId = task.getProcessInstanceId();
        runtimeService.updateBusinessStatus(processInstanceId, "老板审批完成");
        taskService.complete(task.getId(), map);
        log.info("老板审批成功任务{}", taskId);
        return AjaxResult.success("老板审批成功任务" + taskId);
    }

    /**
     * 13. 老板拒绝
     *
     * @param taskId 任务ID
     * @return
     */
    @PostMapping("/bossRefuse/{taskId}/{bossId}")
    public AjaxResult bossRefuse(@PathVariable("taskId") String taskId, @PathVariable String bossId) {
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(bossId).singleResult();
        if (task == null) {
            log.info("老板没有任务");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("result", "驳回");
        taskService.complete(task.getId(), map);
        runtimeService.updateBusinessStatus(task.getProcessInstanceId(), "老板审批拒绝");
        log.info("领导审批拒绝任务{}", taskId);
        return AjaxResult.success("领导审批拒绝任务" + taskId);
    }

    /**
     * 14. 员工再次申请请假
     *
     * @param processId 流程id
     * @param day
     * @return
     */
    @PostMapping("/applyAgain/{processId}/{day}/{employeeId}/{leaderId}")
    public AjaxResult applyAgain(@PathVariable("processId") String processId, @PathVariable("day") Integer day, @PathVariable("employeeId") String employeeId, @PathVariable("leaderId") String leaderId) {
        Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
        if (task == null) {
            log.info("员工没有任务");
        }
        // 提交请假申请
        Map<String, Object> map = new HashMap<>();
        map.put("day", day);
        map.put("employeeId", employeeId);
        map.put("leaderId", leaderId);
        map.put("groups", Arrays.asList("leader"));
        taskService.complete(task.getId(), map);
        runtimeService.updateBusinessStatus(task.getProcessInstanceId(), "审批中");
        log.info("员工再次请假申请,任务{}", task.getId());
        return AjaxResult.success("员工再次请假申请,任务" + task.getId());
    }

    /**
     * 15. 生成流程图
     *
     * @param processId 流程ID
     */
    @GetMapping("/processDiagram/{processId}")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, @PathVariable("processId") String processId) throws Exception {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService.createExecutionQuery().processInstanceId(InstanceId).list();
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(),
                engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
        OutputStream os = null;
        try {
            BufferedImage image = ImageIO.read(inputStream);
            httpServletResponse.setContentType("image/png");
            os = httpServletResponse.getOutputStream();
            if (image != null) {
                ImageIO.write(image, "png", os);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.flush();
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

